package v1

import (
	"context"
	"fmt"
	"reflect"

	"github.com/go-logr/logr"
	rbacv1 "k8s.io/api/rbac/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

// sa声明信息
type OwnClusterRole struct {
	Name string `json:"name"`

	// Rules holds all the PolicyRules for this Role
	// +optional
	Rules []rbacv1.PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"`
}

type OwnClusterRoleStatus struct {
	Name   string `json:"name"`
	Status bool   `json:"status"`
	//PolicyRule []rbacv1.PolicyRule `json:"policyrule" protobuf:"bytes,2,rep,name=policyrule"`
}

func (o *OwnClusterRole) MakeOwnResource(instance *IngressNginx, logger logr.Logger,
	scheme *runtime.Scheme) (interface{}, error) {

	// new a PVC object
	i := &rbacv1.ClusterRole{
		// metadata field inherited from owner Unit
		ObjectMeta: metav1.ObjectMeta{Name: o.Name, Labels: instance.Labels},
		Rules:      o.Rules,
	}

	// add ControllerReference for sts，the owner is Unit object
	/*
		if err := controllerutil.SetControllerReference(instance, i, scheme); err != nil {
			msg := fmt.Sprintf("set controllerReference for %s/%s failed", i.Kind, i.Name)
			logger.Error(err, msg)
			return nil, err
		}
	*/

	return i, nil
}

// Check if the ownPVC already exists
func (o *OwnClusterRole) OwnResourceExist(instance *IngressNginx, client client.Client,
	logger logr.Logger) (bool, interface{}, error) {

	found := &rbacv1.ClusterRole{}
	//logger.Info("ClusterRole Info", "Name", o.Name)
	err := client.Get(context.Background(), types.NamespacedName{Name: o.Name}, found)
	if err != nil {
		if errors.IsNotFound(err) {
			//msg := fmt.Sprintf("ClusterRole/%s not found", o.Name)
			//logger.Error(err, msg)
			return false, nil, nil
		}

		msg := fmt.Sprintf("%s/%s found, but with error", found.Kind, found.Name)
		logger.Error(err, msg)
		return true, found, err
	}
	return true, found, nil
}

func (o *OwnClusterRole) UpdateOwnResourceStatus(instance *IngressNginx, client client.Client,
	logger logr.Logger) (*IngressNginx, error) {

	found := &rbacv1.ClusterRole{}
	err := client.Get(context.TODO(), types.NamespacedName{Name: o.Name}, found)
	if err != nil {
		//msg := fmt.Sprintf("Get ClusterRole/%s failed", o.Name)
		//logger.Error(err, msg)
		if errors.IsNotFound(err) {
			instance.Status.RelationResourceStatus.ClusterRole.Status = false
			instance.Status.LastUpdateTime = metav1.Now()
			return instance, nil
		}
		return instance, err
	}

	//instance.Status.RelationResourceStatus.ClusterRole.PolicyRule = found.Rules

	return instance, nil
}

// apply this own resource, create or update
func (o *OwnClusterRole) ApplyOwnResource(instance *IngressNginx, client client.Client,
	logger logr.Logger, scheme *runtime.Scheme) error {

	// assert if PVC exist
	exist, found, err := o.OwnResourceExist(instance, client, logger)
	if err != nil {
		return err
	}

	// make PVC object
	i, err := o.MakeOwnResource(instance, logger, scheme)
	if err != nil {
		return err
	}
	new := i.(*rbacv1.ClusterRole)

	// apply the PVC object just make
	if !exist {
		// if PVC not exist，then create it
		msg := fmt.Sprintf("%s/%s not found, create it!", new.Kind, new.Name)
		logger.Info(msg)

		if err := client.Create(context.TODO(), new); err != nil {
			return err
		}
		instance.Status.RelationResourceStatus.ClusterRole.Status = true
		instance.Status.RelationResourceStatus.ClusterRole.Name = o.Name

		return nil
	}
	old := found.(*rbacv1.ClusterRole)
	// if PVC exist with change，then try to update it
	if !reflect.DeepEqual(new.Rules, old.Rules) {
		msg := fmt.Sprintf("Updating %s/%s", new.Kind, new.Name)
		logger.Info(msg)
		return client.Update(context.TODO(), new)
	}
	return nil
}
